package com.alibaba.citrus.ots.platform.enhance.facade.service.diff.activity;

import com.alibaba.citrus.ots.common.annotation.FacadeInvoker;
import com.alibaba.citrus.ots.common.annotation.ProcessInvoker;
import com.alibaba.citrus.ots.platform.enhance.facade.service.diff.activity.constant.ActivityFlowEnum;
import com.alibaba.citrus.ots.platform.enhance.facade.service.diff.common.constant.DiffResultStatus;
import com.alibaba.citrus.ots.platform.enhance.facade.service.diff.common.constant.DiffTypeEnum;
import com.alibaba.citrus.ots.platform.enhance.facade.service.diff.common.DiffCheckRequest;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.constant.DiffStrategyRouteDict;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.router.DiffCheckProcessor;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.router.DiffStrategyRoute;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.activity.template.ActivityUseRecordRelationStandardTemplate;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.dataParser.DataParserManager;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.template.SalesOrderLineStandardTemplate;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.template.StandardTemplateDiff;
import com.alibaba.citrus.ots.platform.enhance.repository.ActivityQueryRepository;
import com.alibaba.citrus.ots.platform.enhance.repository.DiffErrorRepository;
import com.alibaba.citrus.ots.platform.enhance.repository.SalesOrderLineQueryServiceRepository;
import com.alibaba.citrus.ots.platform.enhance.repository.model.diff.common.FieldLineDTO;
import com.alibaba.citrus.ots.platform.enhance.repository.model.diff.common.SalesOrderLineDTO;
import com.alibaba.citrus.ots.platform.enhance.repository.model.diff.salesOrder.QuerySalesOrderLine;
import com.alibaba.citrus.ots.platform.enhance.repository.model.diff.activity.QuerySalesOrderLineParam;
import com.alibaba.citrus.ots.platform.enhance.repository.model.diff.activity.QueryActivityUseRecordParam;
import com.alibaba.citrus.ots.platform.enhance.repository.model.diff.common.DiffErrorDTO;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.epoch.app.otsplatformenhance.activitydiffcheck.service.ActivityDiffCheckService;
import com.epoch.app.otsplatformenhance.model.dto.ActivityDiffCheckRequest;
import com.epoch.app.otspricecenter.model.enums.ActivityTypeEnum;
import com.epoch.app.otspricecenter.sdo.ActivityUseRecordRelationSDO;
import org.apache.commons.collections4.CollectionUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: qiaojiange
 * @create: 2022/01/18
 * @Description 活动对账
 */
@Service
@Primary
public class ActivityDiffCheckServiceImpl implements ActivityDiffCheckService {

    /**
     * 日志
     */
    private Log log = Log.getLogger(ActivityDiffCheckServiceImpl.class);

    @Resource
    private ActivityQueryRepository activityQueryRepository;

    @Resource
    private DataParserManager dataParserManager;

    @Resource
    private StandardTemplateDiff standardTemplateDiff;

    @Resource
    private DiffStrategyRoute diffStrategyRoute;

    @Resource
    private SalesOrderLineQueryServiceRepository salesOrderLineQueryServiceRepository;

    @Resource
    private ApplicationContext applicationContext;

    @Resource
    private DiffErrorRepository diffErrorRepository;

    @Override
    @FacadeInvoker
    public Result<String> activityOccupyDiffCheck(ActivityDiffCheckRequest request) {
        // 定时任务（ScheduleX ），扫描新增的活动使用记录。---》 DAO,按modify时间升序，可以支持分页查询 20。
        // 存到对账记录表中 --->暂时不做
        // 从活动记录表中的提取数据和标准模板对比 ---》解决重和漏的问题 ---》峥嵘
        // 从活动记录表中的 sequenceNO， 子单 ID 查询销售子单 ---》获取销售子单 ---》韩国
        // 销售子单和销售子单的标准模板对比 ---》解决重和漏的问题 ---》韩国
        // 把活动使用记录中的字段根据规则引擎生成表达式 b1 ---》峥嵘
        // 把销售子单中的字段根据规则引擎生成表达式 b2 ---》韩国
        // 对比b1 和 b2 --->解决错的问题 ---》峥嵘


        QueryActivityUseRecordParam useRecordDTO = new QueryActivityUseRecordParam();
        useRecordDTO.setLimit(request.getLimit());
        useRecordDTO.setStatus(ActivityFlowEnum.ACTIVITY_OCCUPY.getStatus());
        useRecordDTO.setStart(request.getStart());
        useRecordDTO.setStartModifyTime(request.getStartDiffDate());
        useRecordDTO.setEndModifyTime(request.getEndDiffDate());
        Result<List<ActivityUseRecordRelationSDO>> listResult = activityQueryRepository.queryActivityUseRecord(useRecordDTO);

        int total = listResult.getTotal();
        boolean isFirst = true;
        while (useRecordDTO.getStart() < total) {
            if (!isFirst) {
                listResult = activityQueryRepository.queryActivityUseRecord(useRecordDTO);
            }else{
                isFirst = false;
            }
            if (CollectionUtils.isNotEmpty(listResult.getResult())) {

                //过滤出特价活动
                filterSpecialActivityData(listResult);

                if (CollectionUtils.isNotEmpty(listResult.getResult())) {

                    List<DiffErrorDTO> diffErrorDTOList = new ArrayList<>();
                    //do diff check
                    for (ActivityUseRecordRelationSDO recordRelationSDO : listResult.getResult()) {
                        DiffErrorDTO diffErrorDTO = applicationContext.getBean(ActivityDiffCheckServiceImpl.class)
                                .doDiff(recordRelationSDO);
                        if (Objects.nonNull(diffErrorDTO.getStatus())
                                && diffErrorDTO.getStatus().equals(DiffResultStatus.FAIL.getStatus())) {
                            diffErrorDTOList.add(diffErrorDTO);
                        }
                    }

                    //保存错误对账记录
                    if (CollectionUtils.isNotEmpty(diffErrorDTOList)) {
                        //批量保存差错记录
                        diffErrorRepository.batchSaveDiffErrorList(diffErrorDTOList);
                    }
                }

            }

            useRecordDTO.setStart(useRecordDTO.getStart() + useRecordDTO.getLimit());
        }

        return Result.success("true");
    }

    /**
     * 过滤出特价活动
     * @param listResult
     */
    private void filterSpecialActivityData(Result<List<ActivityUseRecordRelationSDO>> listResult) {

        //根据活动号进行聚合
        Set<String> activityIds = listResult.getResult().stream().map(ActivityUseRecordRelationSDO::getActivityId).collect(Collectors.toSet());
        QueryActivityUseRecordParam queryActivityUseRecordDTO = new QueryActivityUseRecordParam();
        queryActivityUseRecordDTO.setActivityIds(new ArrayList<>(activityIds));
        queryActivityUseRecordDTO.setActivityType(ActivityTypeEnum.SPECIAL_OFFER_PRICE.getValue());
        //查询特价活动
        Result<List<String>> result = activityQueryRepository.filterActivityIdsByActivityFacadeCode(queryActivityUseRecordDTO);

        List<String> specialOfferPriceActivityIds = result.getResult();
        //没有查到特价活动时，置为空
        if (CollectionUtils.isEmpty(specialOfferPriceActivityIds)) {
            listResult.setResult(new ArrayList<>());
        }

        //过滤数据
        listResult.setResult(
                listResult.getResult().stream().filter(e -> specialOfferPriceActivityIds.contains(e.getActivityId())).collect(Collectors.toList())
        );
    }

    /**
     * 真正的对账
     *
     * @param recordRelationSDO    活动使用记录
     * @return
     */
    @ProcessInvoker
    public DiffErrorDTO doDiff(ActivityUseRecordRelationSDO recordRelationSDO) {
        try {
            //1. 数据抽取
            Map<String, FieldLineDTO> activityRecordDataMap = dataParserManager.parse(recordRelationSDO);
            //2. 和标准模板进行对比，检查是否缺的问题。 缺失有两个问题： a. 字段缺失， b. 数值缺失
            standardTemplateDiff.check(ActivityUseRecordRelationStandardTemplate.obtainTemplateFieldSet(),
                    activityRecordDataMap, null);

            //3. 获取销售子单
            SalesOrderLineDTO salesOrderLineDTO = obtainSalesOrderLineDTO(recordRelationSDO);
            //4. 从销售子单上抽取数据
            Map<String, FieldLineDTO> salesOrderLineDataMap = dataParserManager.parse(salesOrderLineDTO);
            //5. 和标准模板进行对比，检查是否缺的问题。
            Result<String> checkResult = standardTemplateDiff.check(SalesOrderLineStandardTemplate.obtainTemplateFieldSet(), salesOrderLineDataMap, null);
            if (!checkResult.isSuccess()) {
                //埋点
                log.error("ActivityDiffCheckServiceImpl#activityDiffCheck_fail");
                DiffErrorDTO diffErrorDto = buildDiffErrorDTO(recordRelationSDO, activityRecordDataMap,
                        salesOrderLineDataMap, checkResult.getMessage());
                return diffErrorDto;
            }

            //6. 对账
            DiffCheckRequest diffCheckRequest = buildDiffCheckRequest(activityRecordDataMap, salesOrderLineDataMap);
            DiffCheckProcessor diffCheckProcessor = diffStrategyRoute.obtainProcessor(diffCheckRequest);
            Result<String> diffResult = diffCheckProcessor.diff(diffCheckRequest);
            if (!diffResult.isSuccess()) {
                //埋点
                log.error("ActivityDiffCheckServiceImpl#activityDiffCheck_fail");
                DiffErrorDTO diffErrorDto = buildDiffErrorDTO(recordRelationSDO, activityRecordDataMap,
                        salesOrderLineDataMap, diffResult.getMessage());
                return diffErrorDto;
            }
        } catch (Exception e) {
            //埋点
            log.error("ActivityDiffCheckServiceImpl#activityDiffCheck_fail");
            log.error("对账出错了", e);
            DiffErrorDTO diffErrorDto = buildDiffErrorDTO(recordRelationSDO, null,
                    null, "对账出异常了！");
            return diffErrorDto;
        }
        return DiffErrorDTO.builder().build();
    }

    /**
     * 获取销售子单行
     * @param recordRelationSDO 活动记录数据
     * @return 销售子单
     */
    private SalesOrderLineDTO obtainSalesOrderLineDTO(ActivityUseRecordRelationSDO recordRelationSDO) {
        Map<String, SalesOrderLineDTO> resultMap = new HashMap<>();

        List<QuerySalesOrderLine> querySalesOrderLineList = new ArrayList<>();
        QuerySalesOrderLine querySalesOrderLine = new QuerySalesOrderLine();
        querySalesOrderLine.setForecastSubOrderId(recordRelationSDO.getSubOrderId());
        querySalesOrderLine.setSequenceNo(recordRelationSDO.getSequenceNo());
        querySalesOrderLineList.add(querySalesOrderLine);


        QuerySalesOrderLineParam querySalesOrderLineParam = new QuerySalesOrderLineParam();
        querySalesOrderLineParam.setQuerySalesOrderLines(querySalesOrderLineList);
        querySalesOrderLineParam.setStart(0);
        querySalesOrderLineParam.setLimit(querySalesOrderLineList.size());
        Result<List<SalesOrderLineDTO>> listResult = salesOrderLineQueryServiceRepository.querySalesOrderLine(querySalesOrderLineParam);

        if (CollectionUtils.isEmpty(listResult.getResult())) {
            return null;
        }

        //一个销售子单下多个活动时的处理
        listResult.getResult().forEach(e -> {
            String activityCode = e.getActivityCode();
            if (activityCode.contains("/")) {
                splitSalesOrderLine(e, resultMap);
            } else {
                resultMap.put(e.getActivityCode() + "_" + e.getFrontOrderLineId() + "_" + JSONObject.parseObject(e.getFeatures(), Map.class).get("sequenceNo"), e);
            }
        });

        return resultMap.get(recordRelationSDO.getActivityId()+"_"+recordRelationSDO.getSubOrderId()+"_"+recordRelationSDO.getSequenceNo());
    }

    @NotNull
    private DiffCheckRequest buildDiffCheckRequest(Map<String, FieldLineDTO> activityRecordDataMap, Map<String, FieldLineDTO> salesOrderLineDataMap) {
        DiffCheckRequest diffCheckRequest = new DiffCheckRequest();
        diffCheckRequest.setStrategyRoute(DiffStrategyRouteDict.ACTIVITY_DIFF_SALES_ORDER_LINE.getStrategyCode());
        diffCheckRequest.setDiffDesc(DiffTypeEnum.ACTIVITY_DIFF_SALES_ORDER_LINE.getDiffDesc());
        diffCheckRequest.setBaseLineDataMap(activityRecordDataMap);
        diffCheckRequest.setDiffDataMap(salesOrderLineDataMap);
        diffCheckRequest.setDiffType(DiffTypeEnum.ACTIVITY_DIFF_SALES_ORDER_LINE.getDiffType());
        return diffCheckRequest;
    }

    /**
     * 构造差错 DTO
     *
     * @param recordRelationSDO     活动使用记录
     * @param activityRecordDataMap 活动使用记录映射
     * @param salesOrderLineDataMap 销售子单记录映射
     * @param diffResult            对账结果信息
     * @return 对账信息
     */
    private DiffErrorDTO buildDiffErrorDTO(ActivityUseRecordRelationSDO recordRelationSDO,
                                           Map<String, FieldLineDTO> activityRecordDataMap,
                                           Map<String, FieldLineDTO> salesOrderLineDataMap, String diffResult) {
        //1. 封装差错记录
        DiffErrorDTO diffErrorDto = DiffErrorDTO.builder()
                .diffType(DiffTypeEnum.ACTIVITY_DIFF_SALES_ORDER_LINE.getDiffType())
                //业务流程描述
                .diffDesc(DiffTypeEnum.ACTIVITY_DIFF_SALES_ORDER_LINE.getDiffDesc())
                //对账结果描述
                .result(diffResult)
                .diffData1(JSON.toJSONString(activityRecordDataMap))
                .diffData2(JSON.toJSONString(salesOrderLineDataMap))
                .strategyRoute(DiffStrategyRouteDict.ACTIVITY_DIFF_SALES_ORDER_LINE.getStrategyCode())
                .mainOrderId(recordRelationSDO.getMainOrderId())
                .subOrderId(recordRelationSDO.getSubOrderId())
                .activityId(recordRelationSDO.getActivityId())
                .activityTermId(recordRelationSDO.getActivityTermId())
                .sequence(recordRelationSDO.getSequenceNo())
                .status(DiffResultStatus.FAIL.getStatus()).build();
        return diffErrorDto;
    }


    /**
     * 根据预报子单和批次号获取销售子单数据
     *
     * @param activityUseRecordRelationSDOS 活动使用记录关系 DTOS
     * @return
     */
    private Map<String, SalesOrderLineDTO> obtainSalesOrderLineDTOMap(List<ActivityUseRecordRelationSDO> activityUseRecordRelationSDOS) {

        Map<String, SalesOrderLineDTO> resultMap = new HashMap<>();

        if (CollectionUtils.isEmpty(activityUseRecordRelationSDOS)) {
            return resultMap;
        }

        List<QuerySalesOrderLine> querySalesOrderLines = activityUseRecordRelationSDOS.stream().map(e -> {
            QuerySalesOrderLine querySalesOrderLine = new QuerySalesOrderLine();
            querySalesOrderLine.setForecastSubOrderId(e.getSubOrderId());
            querySalesOrderLine.setSequenceNo(e.getSequenceNo());
            return querySalesOrderLine;
        }).collect(Collectors.toList());

        QuerySalesOrderLineParam querySalesOrderLineParam = new QuerySalesOrderLineParam();
        querySalesOrderLineParam.setQuerySalesOrderLines(querySalesOrderLines);
        querySalesOrderLineParam.setStart(0);
        querySalesOrderLineParam.setLimit(querySalesOrderLines.size());
        Result<List<SalesOrderLineDTO>> listResult = salesOrderLineQueryServiceRepository.querySalesOrderLine(querySalesOrderLineParam);

        if (CollectionUtils.isEmpty(listResult.getResult())) {
            return resultMap;
        }


        //一个销售子单下多个活动时的处理
        listResult.getResult().forEach(e -> {
            String activityCode = e.getActivityCode();
            if (activityCode.contains("/")) {
                splitSalesOrderLine(e, resultMap);
            } else {
                resultMap.put(e.getActivityCode() + "_" + e.getFrontOrderLineId() + "_" + JSONObject.parseObject(e.getFeatures(), Map.class).get("sequenceNo"), e);
            }

        });

        return resultMap;
    }

    //一订单多活动处理
    private void splitSalesOrderLine(SalesOrderLineDTO salesOrderLineDTO, Map<String, SalesOrderLineDTO> resultMap) {
        String activityCode = salesOrderLineDTO.getActivityCode();
        for (String subActivityCode : activityCode.split("/")) {
            SalesOrderLineDTO subSalesOrderDTO = new SalesOrderLineDTO();
            BeanUtils.copyProperties(salesOrderLineDTO, subSalesOrderDTO);
            subSalesOrderDTO.setActivityCode(subActivityCode);
            resultMap.put(subSalesOrderDTO.getActivityCode() + "_" + subSalesOrderDTO.getFrontOrderLineId() + "_" + JSONObject.parseObject(subSalesOrderDTO.getFeatures(), Map.class).get("sequenceNo"), subSalesOrderDTO);
        }
    }
}
